<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CIO类说明文档</title>
    <style>
        body {
            font-family: 'Microsoft YaHei', Arial, sans-serif;
            line-height: 1.6;
            color: #333;
            max-width: 1200px;
            margin: 0 auto;
            padding: 20px;
        }
        h1 {
            color: #0066cc;
            border-bottom: 2px solid #0066cc;
            padding-bottom: 10px;
        }
        h2 {
            color: #0066cc;
            border-bottom: 1px solid #ddd;
            padding-bottom: 5px;
            margin-top: 30px;
        }
        h3 {
            color: #333;
            margin-top: 20px;
        }
        pre {
            background-color: #f5f5f5;
            padding: 10px;
            border-radius: 5px;
            overflow-x: auto;
        }
        code {
            font-family: Consolas, Monaco, 'Andale Mono', monospace;
            background-color: #f5f5f5;
            padding: 2px 4px;
            border-radius: 3px;
        }
        table {
            border-collapse: collapse;
            width: 100%;
            margin: 20px 0;
        }
        th, td {
            border: 1px solid #ddd;
            padding: 8px 12px;
            text-align: left;
        }
        th {
            background-color: #f2f2f2;
        }
        .note {
            background-color: #e6f7ff;
            border-left: 4px solid #1890ff;
            padding: 10px;
            margin: 15px 0;
        }
        .warning {
            background-color: #fff7e6;
            border-left: 4px solid #fa8c16;
            padding: 10px;
            margin: 15px 0;
        }
        .diagram {
            text-align: center;
            margin: 20px 0;
        }
    </style>
</head>
<body>
    <h1>CIO类说明文档</h1>
    
    <h2>1. 类概述</h2>
    <p>CIO类是一个基于QThread的线程类，主要用于通过Modbus RTU协议与串口设备进行通信，实现数字输入(DI)和数字输出(DO)的读取和控制。该类在BA项目中用于监控和控制外部设备的状态。</p>
    
    <div class="note">
        <p><strong>版本信息：</strong> 当前版本为 0.1</p>
    </div>

    <h2>2. 类继承关系</h2>
    <p>CIO类继承自Qt的QThread类，这使得它能够在独立的线程中运行，不会阻塞主线程。</p>
    <pre><code>class CIO : public QThread</code></pre>

    <h2>3. 主要功能</h2>
    <ul>
        <li>通过串口与Modbus设备通信</li>
        <li>读取数字输入(DI)状态</li>
        <li>控制数字输出(DO)状态</li>
        <li>读取数字输出(DO)状态</li>
        <li>处理Modbus RTU协议数据帧</li>
        <li>计算CRC16校验</li>
    </ul>

    <h2>4. 成员变量说明</h2>
    <table>
        <tr>
            <th>变量名</th>
            <th>类型</th>
            <th>说明</th>
        </tr>
        <tr>
            <td>m_pSerial</td>
            <td>CXnSerial*</td>
            <td>串口通信对象指针</td>
        </tr>
        <tr>
            <td>m_pIOModule</td>
            <td>CIOModule*</td>
            <td>IO模块对象指针，用于存储DI/DO状态</td>
        </tr>
        <tr>
            <td>m_bSerialOK</td>
            <td>bool</td>
            <td>串口是否打开成功的标志</td>
        </tr>
        <tr>
            <td>m_strPortName</td>
            <td>QString</td>
            <td>串口名称，Windows下默认为"com4"，Linux下默认为"/dev/ttyS5"</td>
        </tr>
        <tr>
            <td>m_iBaudRate</td>
            <td>int</td>
            <td>波特率，默认为115200</td>
        </tr>
        <tr>
            <td>m_iDataBits</td>
            <td>int</td>
            <td>数据位，默认为8</td>
        </tr>
        <tr>
            <td>m_iStopBits</td>
            <td>int</td>
            <td>停止位，默认为1</td>
        </tr>
        <tr>
            <td>m_iParity</td>
            <td>int</td>
            <td>校验位，默认为0（无校验）</td>
        </tr>
        <tr>
            <td>m_iRtscts</td>
            <td>int</td>
            <td>硬件流控制，默认为0（禁用）</td>
        </tr>
        <tr>
            <td>m_bPrintMsg</td>
            <td>bool</td>
            <td>是否打印调试信息的标志</td>
        </tr>
        <tr>
            <td>m_iSerialDataLen</td>
            <td>int</td>
            <td>串口数据长度</td>
        </tr>
        <tr>
            <td>m_pSerialDataBuf</td>
            <td>char*</td>
            <td>串口数据缓冲区</td>
        </tr>
        <tr>
            <td>m_logCount</td>
            <td>int</td>
            <td>日志计数器，用于限制日志输出数量</td>
        </tr>
    </table>

    <h2>5. 公共方法</h2>
    
    <h3>5.1 构造函数和析构函数</h3>
    <pre><code>CIO::CIO(QObject *parent = 0);
CIO::~CIO();</code></pre>
    <p>构造函数初始化所有成员变量，创建串口对象和数据缓冲区。析构函数释放所有动态分配的资源。</p>
    
    <h3>5.2 初始化方法</h3>
    <pre><code>int CIO::init();</code></pre>
    <p>初始化IO模块，获取全局IO模块对象。返回值为0表示初始化成功，负值表示失败。</p>
    
    <h3>5.3 线程执行方法</h3>
    <pre><code>void CIO::run();</code></pre>
    <p>QThread的虚函数重写，包含线程的主要执行逻辑：</p>
    <ul>
        <li>打开串口</li>
        <li>循环执行以下操作：
            <ul>
                <li>读取DI状态（类型1，功能码0x02）</li>
                <li>写入DO状态（类型2，功能码0x05）</li>
                <li>读取DO状态（类型3，功能码0x01）</li>
            </ul>
        </li>
        <li>在第10次循环时，将当前DO状态同步到DO控制值</li>
    </ul>

    <h2>6. 私有方法</h2>
    
    <h3>6.1 调试信息打印</h3>
    <pre><code>void CIO::printSendInfo(char* pDataBuf, int iLen);
void CIO::printReceInfo(char* pDataBuf, int iLen);</code></pre>
    <p>这两个方法用于打印发送和接收的数据报文，以十六进制格式显示。只有在m_bPrintMsg为true且m_logCount小于50时才会打印。</p>
    
    <h3>6.2 数据报文处理</h3>
    <pre><code>int CIO::formDatagram(int iType, int iModbusID, int iDOIndex = 0, quint16 usDOCtrlVal = 0);
int CIO::analyDatagram(int iType, int iModbusID, char* pDataBuf, int iLen);
void CIO::readDataFromSerial();</code></pre>
    
    <p><strong>formDatagram</strong>: 根据不同的操作类型构造Modbus RTU数据报文：</p>
    <ul>
        <li>类型1（0x02）：读取DI状态</li>
        <li>类型2（0x05）：写入DO状态</li>
        <li>类型3（0x01）：读取DO状态</li>
    </ul>
    
    <p><strong>analyDatagram</strong>: 解析接收到的Modbus RTU数据报文，提取DI或DO状态信息，并更新到m_pIOModule中。</p>
    
    <p><strong>readDataFromSerial</strong>: 从串口读取数据，支持多次尝试读取以获取完整的数据报文。</p>
    
    <h3>6.3 异常处理</h3>
    <pre><code>int CIO::prepareExcepDatagram(uchar uaExcepCode);</code></pre>
    <p>准备Modbus异常响应报文，当发生错误时返回给主站。</p>
    
    <h3>6.4 数据转换和校验</h3>
    <pre><code>int CIO::addParam2Buffer(char* pData, quint16 uiParamVal);
int CIO::getParamFromBuffer(char* pData, quint16& uiParamVal);
int CIO::addModbusCRC2Buffer(char *pData, int iLen);
quint16 CIO::Modbus_CRC16(char *pData, quint16 usLen);</code></pre>
    
    <p><strong>addParam2Buffer</strong>: 将16位无符号整数添加到缓冲区，高字节在前。</p>
    <p><strong>getParamFromBuffer</strong>: 从缓冲区提取16位无符号整数，高字节在前。</p>
    <p><strong>addModbusCRC2Buffer</strong>: 计算CRC16校验值并添加到缓冲区末尾。</p>
    <p><strong>Modbus_CRC16</strong>: 计算Modbus RTU协议的CRC16校验值，使用查表法提高效率。</p>

    <h2>7. 通信协议说明</h2>
    <p>CIO类使用Modbus RTU协议通过串口与设备通信。主要使用以下功能码：</p>
    <table>
        <tr>
            <th>功能码</th>
            <th>操作类型</th>
            <th>说明</th>
        </tr>
        <tr>
            <td>0x01</td>
            <td>读取DO状态</td>
            <td>读取线圈状态</td>
        </tr>
        <tr>
            <td>0x02</td>
            <td>读取DI状态</td>
            <td>读取离散输入状态</td>
        </tr>
        <tr>
            <td>0x05</td>
            <td>写入DO状态</td>
            <td>写单个线圈</td>
        </tr>
    </table>
    
    <h3>7.1 站号和地址说明</h3>
    <div class="note">
        <p>1. 站号：1-网口一排 和 2-扩展板</p>
        <p>2. 8路DI对应地址：00 ~ 08</p>
        <p>3. 6路DO对应地址：00 ~ 06</p>
        <p>DO状态：闭合:0 断开:1</p>
        <p>DI状态：24V接入:1 24V未接:0</p>
    </div>
    
    <h3>7.2 数据帧格式</h3>
    <p>Modbus RTU数据帧格式如下：</p>
    <table>
        <tr>
            <th>字段</th>
            <th>长度(字节)</th>
            <th>说明</th>
        </tr>
        <tr>
            <td>站号</td>
            <td>1</td>
            <td>设备地址，1或2</td>
        </tr>
        <tr>
            <td>功能码</td>
            <td>1</td>
            <td>0x01, 0x02, 0x05</td>
        </tr>
        <tr>
            <td>数据</td>
            <td>n</td>
            <td>根据功能码不同而不同</td>
        </tr>
        <tr>
            <td>CRC校验</td>
            <td>2</td>
            <td>低字节在前，高字节在后</td>
        </tr>
    </table>

    <h2>8. 使用示例</h2>
    <p>以下是CIO类的基本使用流程：</p>
    <pre><code>// 创建CIO对象
CIO *pIO = new CIO();

// 初始化
if (pIO->init() == 0) {
    // 启动线程
    pIO->start();
    
    // 线程会自动执行run()方法，循环读取DI状态和控制DO状态
    // 可以通过全局IO模块对象访问DI和DO状态
    CIOModule *pIOModule = ::getIOModule();
    
    // 读取DI状态
    quint16 diValue = pIOModule->usDI[0]; // 读取第一路DI状态
    
    // 控制DO状态
    pIOModule->usDOCtrl[0] = 1; // 设置第一路DO为断开状态
}

// 停止线程并释放资源
pIO->terminate();
pIO->wait();
delete pIO;</code></pre>

    <h2>9. 注意事项</h2>
    <div class="warning">
        <p>1. CIO类在独立线程中运行，访问共享资源时需要注意线程安全问题。</p>
        <p>2. 串口通信可能会受到外部干扰，需要处理通信错误和超时情况。</p>
        <p>3. 在Windows和Linux平台下，串口名称不同，需要根据实际情况修改。</p>
        <p>4. 修改DO状态时，应通过m_pIOModule->usDOCtrl数组进行，而不是直接修改usDOState数组。</p>
        <p>5. 线程启动后会持续运行，直到程序结束或手动停止线程。</p>
    </div>

    <h2>10. 相关类</h2>
    <p>CIO类与以下类有关联：</p>
    <ul>
        <li><strong>CXnSerial</strong>：串口通信类，提供底层串口操作</li>
        <li><strong>CIOModule</strong>：IO模块类，存储DI和DO状态</li>
        <li><strong>CBMSIO</strong>：与CIO类似的另一个IO处理类</li>
    </ul>

    <div class="note">
        <p>文档创建日期：2024年5月</p>
        <p>适用版本：BA-10-V3.8.0</p>
    </div>
</body>
</html>